<?php
namespace Tbf\Bundle\AjkApiBundle\Listener;
use Doctrine\Common\Annotations\Reader;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Tbf\Bundle\AjkApiBundle\AjkApiHandler;
use Tbf\Bundle\AjkApiBundle\Exception\AjkApiException;
use Tbf\Bundle\TbfBundle\Exception\BusinessException;
use Tbf\Bundle\AjkApiBundle\Annotation\Api;
use Tbf\Bundle\TbfBundle\Service\AnnotationsReader;

/**
 * add api argument named input
 */
class ApiKernelListener implements EventSubscriberInterface
{
    protected $reader;
    protected $handler;
    public function __construct(AnnotationsReader $reader,AjkApiHandler $handler)
    {
        $this->reader = $reader;
        $this->handler = $handler;
    }
    public static function getSubscribedEvents()
    {
        return array(
            KernelEvents::CONTROLLER=>'onArgument',
            KernelEvents::VIEW=>'onReturn',
            KernelEvents::EXCEPTION=>'onException',
        );
    }
    //修改输入
    public function onArgument(FilterControllerEvent $event)
    {
        $api_config = $this->reader->getCallableAnnotation($event->getController(),
            'Tbf\Bundle\AjkApiBundle\Annotation\Api');
        $event->getRequest()->attributes->set('ajk_api_annotation',$api_config);
        if (!$this->checkApiType($event->getRequest(),Api::TYPE_INPUT)) {
            return;
        }
        $request = $event->getRequest();
        $data = $this->handler->inputFromRequest($request,$api_config);
        $request->attributes->set('input',$data);
    }
    //修改输出
    public function onReturn(GetResponseForControllerResultEvent $event)
    {
        if (!$this->checkApiType($event->getRequest(),Api::TYPE_OUTPUT)) {
            return;
        }
        $return = $event->getControllerResult();
        if (!$this->handler->isValidOutput($return)) {
            throw new AjkApiException('ajk api should return array with output and err');
        }
        $resp = $this->handler->outputToResponse($return,$event->getRequest()->attributes->get('ajk_api_annotation'));
        $event->setResponse($resp);
        $event->stopPropagation();
    }
    //处理异常
    public function onException(GetResponseForExceptionEvent $event)
    {
        $exception = $event->getException();
        if (!$exception instanceof BusinessException) {
            return;
        }
        if (!$this->checkApiType($event->getRequest(),Api::TYPE_EXCEPTION)) {
            return;
        }
        $msg = $exception->getMessage();
        $resp = $this->handler->outputToResponse(array(null,$msg),$event->getRequest()->attributes->get('ajk_api_annotation'));
        $event->setResponse($resp);
        $event->stopPropagation();
    }
    public function isValidReturn($return)
    {
        if (!is_array($return)) {
            return false;
        }
        if (count($return)!==2) {
            return false;
        }
        if ( !(is_null($return[1])||is_string($return[1])) ) {
            return false;
        }

        return true;
    }

    protected function checkApiType(Request $request,$expect_type){
        $api_config = $request->attributes->get('ajk_api_annotation');
        if ($api_config===null){
            return false;
        }
        if (!in_array($expect_type,$api_config->types)){
            return false;
        }
        return true;
    }
}
